#include<iostream>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <cstring>
#include <unistd.h>
#include <sys/select.h>
#include <vector>
#include <string>
//#include <process.h>
#include <stdio.h>
#include <fstream>  

#define BufferSize 4096

using namespace std;

struct MySelf
{
	string name;
	vector<string> friendlist;
	int server_sockfd;
};

class WeChatClient
{
	MySelf myself;
	string model;
	int sockfd;
    fd_set readSet;
	private:
		void LogicalProcess(string info);
		
		void RegisterPage();
		void LoginPage();
		void LoginOkPage();
		void ChatPage();
		void GroupChatPage();
		void ReceiveData();
		vector<string> GetFriendFromServer();
		void GetBufferInfo();
		string AcceptInfoFromConsole();
		void SendInfoToServer(string info);
		string AcceptInfoFromServer();
		vector<string> Decode(string s);
		void AddfriendResponse(string friend_request_name);
		void FileTransfer(string friend_name, string file_name);
		void FileTransfer_Confirm(string request_name, string file_name);
		void FileReceive(string file_name);
	public:
		WeChatClient();
		void start(int argc, char * argv[]);
		void HomePage(int argc, char * argv[]);
		//void printReadme();	
};

int main(int argc, char * argv[])
{
	if(argc < 3){
		cerr << "input: " << argv[0] << ", ip, port" << endl;
		return 0;
	}
	WeChatClient wechatClient;
	wechatClient.HomePage(argc, argv);
	return 0;
}

WeChatClient::WeChatClient(){
	model = "Home";
}

//void WeChatClient::printReadme(){
//}
void WeChatClient::HomePage(int argc, char * argv[]){	
	while (1){
		start(argc, argv);
		system("clear");
		printf("请输入编号选择：1.注册，2.登陆，3.退出\n");
		int choose;
		scanf("%d", &choose);
		if ( choose == 1 )
			RegisterPage();
		else if ( choose == 2 )
			LoginPage();
		else if ( choose == 3)
			break;
		else
			printf("请选择正确选项~\n");
	}
}

void WeChatClient::RegisterPage(){
	while (1)
	{
		system("clear");
		// char* user_name;
		// char* password;
		string user_name;
		string password;
		printf("请输入用户名：\n");
		//scanf("%s", &user_name);
		user_name = AcceptInfoFromConsole();
		printf("请输入密码：\n");
		//scanf("%s", &password);
		password = AcceptInfoFromConsole();

		string info = "&register&"+user_name+"&"+password;
		SendInfoToServer(info);
		string response = AcceptInfoFromServer();
		vector<string> info_decode = Decode(response);
		//printf("%s\n",info.c_str());
		getchar();
		if (info_decode[0]=="register")
		{
			if (info_decode[1]=="Ok")
			{
				printf("注册成功，输入1返回主界面，输入2继续注册!\n");
				//getchar();
				char ch = getchar();
				if (ch == '1') return;
			}
			else
			{
				printf("注册失败，该用户名已经存在，输入1返回主界面，输入2继续注册！\n");
				char ch = getchar();
				if (ch == '1') return;
			}
		}
		else
		{
			printf("注册失败，输入1返回主界面，输入2继续注册！\n");
			char ch = getchar();
			if (ch == '1') return;
		}
	}
	
}

void WeChatClient::LoginPage(){
	while (1)
	{
		system("clear");
		printf("欢迎来到登陆界面\n");
		// char* user_name;
		// char* password;
		string user_name;
		string password;
		printf("请输入用户名：\n");
		//scanf("%s", &user_name);
		user_name = AcceptInfoFromConsole();
		printf("请输入密码：\n");
		//scanf("%s", &password);
		password = AcceptInfoFromConsole();

		string info = "&login&"+user_name+"&"+password;
		SendInfoToServer(info);
		string response = AcceptInfoFromServer();
		vector<string> info_decode = Decode(response);
		getchar();
		if (info_decode[0]=="login")
		{
			if (info_decode[1]=="Ok")
			{				
				myself.name = user_name;
				myself.server_sockfd = sockfd;
				myself.friendlist = GetFriendFromServer();
				LoginOkPage();
				return;
			}
			else
			{
				printf("登录失败，该用户名不存在或密码错误，输入1返回主界面，输入2继续登录！\n");
				//getchar();
				char ch = getchar();
				if (ch == '1') return;
			}
		}
		else
		{
			printf("登录失败，请重新登陆，输入1返回主界面，输入2继续登录！\n");
			char ch = getchar();
			if (ch == '1') return;
		}
	}
}

void WeChatClient::LoginOkPage(){
	//system("clear");
	printf("登录成功！\n");
	GetBufferInfo();
	char buffer[BufferSize];
	while (1)
	{	
		//printf("正在监听消息，输入回车继续\n");
		string info;
		string friend_name;
		printf("请按编号选择：1.查看好友，2.添加好友，3.建立聊天，4.传输文件，5.创建群聊，6.退出登陆\n");
		while (1)
		{
			FD_ZERO(&readSet);
			FD_SET(0, &readSet);
			FD_SET(sockfd, &readSet);

			int ret = select(sockfd+1, &readSet, NULL, NULL, 0);
			//如果从套接字上接收到数据
			if(FD_ISSET(sockfd, &readSet)){
				memset(buffer, 0, sizeof(buffer));
			    
				int num = recv(sockfd, buffer, sizeof(buffer), 0);
				if(num > 0){
				    LogicalProcess(buffer);
				}
				//else break;
			}
			else break;			
		}
		int choose;
		scanf("%d", &choose);
		switch(choose){
			case 1:
				printf("您的好友有：\n");
				for (int i = 0; i < myself.friendlist.size(); i++)
					printf("%s\n",myself.friendlist[i].c_str());
				getchar();
				break;
			case 2:
				printf("请输入要添加的好友名字：\n");
				friend_name = AcceptInfoFromConsole();
				info = "&addfriend_request&"+friend_name;
				SendInfoToServer(info);
				printf("您已经发出好友申请，请等待对方回复！\n");
				getchar();
				break;
			case 3:
			{
				printf("请输入聊天的好友名字：\n");
				friend_name = AcceptInfoFromConsole();
				info = "&buildchat&"+friend_name;
				SendInfoToServer(info);
				info = AcceptInfoFromServer();
				vector<string> info_decode = Decode(info);
				if (info_decode[0]=="buildchat")
				{
					if ( info_decode[1] == "Ok")	
					{		
								
						ChatPage();
					}
					else
					{
						printf("无此好友，请重新选择！\n");
						getchar();
					}
				}	
				else
				{
					printf("建立聊天失败，请重新选择！\n");
					getchar();
				}
				break;
			}	
			case 4:
				{
					printf("请输入要传输的好友名字：\n");
					friend_name = AcceptInfoFromConsole();
					printf("请输入要传输的文件名字：\n");
					string filename = AcceptInfoFromConsole();
					printf("请输入要传输的文件路径：\n");
					string path = AcceptInfoFromConsole();
					info = "&filetrans_confirm&"+friend_name+"&"+filename;
					SendInfoToServer(info);
					info = AcceptInfoFromServer();
					vector<string> info_decode = Decode(info);
					if ( info_decode[0]=="filetrans_response" )
					{
						if ( info_decode[2]=="Ok" )
						{
							FileTransfer(friend_name,path + "/" + filename);
						}
						else
						{
							printf("%s\n", info_decode[1].c_str());
							printf("拒绝了您的文件传输请求！\n");
							getchar();
						}
					}
					else
					{
						printf("文件传输失败！\n");
						getchar();
					}
				}
				break;
			case 5:
				{
					printf("请输入要传输的好友名字，以&隔开：\n");
					friend_name = AcceptInfoFromConsole();
					info = "&buildgroup&"+friend_name;
					SendInfoToServer(info);
					info = AcceptInfoFromServer();
					vector<string> info_decode = Decode(info);
					if (info_decode[0]=="buildgroup")
					{
						if ( info_decode[1] == "Ok")	
						{				
							GroupChatPage();
						}
						else
						{
							printf("建立聊天失败，请重新选择！\n");
							getchar();
						}
					}	
					else
					{
						printf("建立聊天失败，请重新选择！\n");
						getchar();
					}
					break;
				}
			case 6:
				close(sockfd);
				return;
				break;
			default:
				printf("输入错误。请重新选择！\n");
				break;
		}
	}
}
void WeChatClient::GetBufferInfo(){
	string info = "&getbuffer&"+myself.name;
	SendInfoToServer(info);
	char buffer[BufferSize];
	printf("以下为缓存消息：\n");
	while (1)
	{	
		FD_ZERO(&readSet);
		FD_SET(0, &readSet);
		FD_SET(sockfd, &readSet);

		int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

		if(FD_ISSET(sockfd, &readSet)){
		    memset(buffer, 0, sizeof(buffer));
		    
		    int num = recv(sockfd, buffer, sizeof(buffer), 0);
		    if(num > 0){
		    	LogicalProcess(buffer);
		    }
			//printf("%s\n",buffer);
		}
		else break;
	}
	//printf("按任意键继续\n");
}
void WeChatClient::ChatPage(){
	//system("clear");
	printf("建立聊天成功！\n");
	char buffer[BufferSize];
	while (1)
	{	
		FD_ZERO(&readSet);
		FD_SET(0, &readSet);
		FD_SET(sockfd, &readSet);

		int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

		//如果终端上可读
		if(FD_ISSET(0, &readSet)){
		    memset(buffer, 0, sizeof(buffer));

		    //从终端读取数据
		    int num = read(0, buffer, sizeof(buffer));

		    if(buffer[num-1] == '\n'){
		        buffer[num-1] = '\0';
		    }

		    if(strcmp(buffer, "quit") == 0){
		        break;
		    }
		    //将数据写入到套接字
		    string send = buffer;
		    string info = "&sendinfo&"+send;
		    SendInfoToServer(info);
		    //write(sockfd, buffer, strlen(buffer));
		}

		//如果从套接字上接收到数据
		if(FD_ISSET(sockfd, &readSet)){
		    memset(buffer, 0, sizeof(buffer));
		    
		    int num = recv(sockfd, buffer, sizeof(buffer), 0);
		    if(num > 0){
		    	LogicalProcess(buffer);
		    }
		}
	}
}

void WeChatClient::GroupChatPage(){
	//system("clear");
	printf("建立群聊成功！\n");
	char buffer[BufferSize];
	while (1)
	{	
		FD_ZERO(&readSet);
		FD_SET(0, &readSet);
		FD_SET(sockfd, &readSet);

		int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

		//如果终端上可读
		if(FD_ISSET(0, &readSet)){
		    memset(buffer, 0, sizeof(buffer));

		    //从终端读取数据
		    int num = read(0, buffer, sizeof(buffer));

		    if(buffer[num-1] == '\n'){
		        buffer[num-1] = '\0';
		    }

		    if(strcmp(buffer, "quit") == 0){
		        break;
		    }
		    //将数据写入到套接字
		    string send = buffer;
		    string info = "&sendgroupinfo&"+send;
		    SendInfoToServer(info);
		    //write(sockfd, buffer, strlen(buffer));
		}

		//如果从套接字上接收到数据
		if(FD_ISSET(sockfd, &readSet)){
		    memset(buffer, 0, sizeof(buffer));
		    
		    int num = recv(sockfd, buffer, sizeof(buffer), 0);
		    if(num > 0){
		    	LogicalProcess(buffer);
		    }
		}
	}
}

void WeChatClient::LogicalProcess(string info){
	vector<string> info_decode = Decode(info);
	string info_to_send;
	//printf("%s\n",info.c_str());
	if ( info_decode[0] == "sendinfo")
	{
		string info_receive = info_decode[1] + ":" + info_decode[2] + "\n";
		write(0, info_receive.c_str(), info_receive.length());
	}
	else if ( info_decode[0] == "addfriend_response" )
	{
		if ( info_decode[1] == "Fail")
		{
			string send = info_decode[2];
			info_to_send = send +"拒绝了您的好友申请\n";
			write(0, info_to_send.c_str(), info_to_send.length());
		}
		else if ( info_decode[1] == "Ok")
		{	
			myself.friendlist.push_back(info_decode[2]);
			string send = info_decode[2];
			info_to_send = send + "已成为您的好友\n";
			write(0, info_to_send.c_str(), info_to_send.length());
		}
		else if ( info_decode[1] == "noThisUser")
		{
			info_to_send = "无此用户\n";
			write(0, info_to_send.c_str(), info_to_send.length());
		}
	}
	else if ( info_decode[0] == "logout")
	{
		info_to_send = info_decode[1]+"已下线\n";
		write(0, info_to_send.c_str(), info_to_send.length());
	}
	else if ( info_decode[0] == "friendlogin")
	{
		info_to_send = info_decode[1]+"已上线\n";
		write(0, info_to_send.c_str(), info_to_send.length());
	}
	else if ( info_decode[0] =="addfriend_request" )
	{
		info_to_send = info_decode[1]+"请求添加您为好友\n";
		write(0, info_to_send.c_str(), info_to_send.length());
		AddfriendResponse(info_decode[1]);
	}
	else if ( info_decode[0] == "filetrans_confirm" )
	{
		string info_to_show = info_decode[1]+"向您发送文件"+info_decode[2]+"\n";
		write(0, info_to_show.c_str(), info_to_show.length());
		FileTransfer_Confirm(info_decode[1], info_decode[2]);
	} 
	// else if ( info_decode[0] == "filetransfer" )
	// {
	// 	string info_to_show = info_decode[1]+"向您发送文件"+info_decode[2]+"\n";
	// 	write(0, info_to_show.c_str(), info_to_show.length());
	// 	FileTransfer_Confirm(info_decode[1]);
	// } 
}

void WeChatClient::AddfriendResponse(string friend_request_name){
	printf("输入1同意添加，输入2拒绝添加\n");
	char ch = getchar();
	if ( ch == '1' )
	{
		string info = "&addfriend_response&"+friend_request_name+"&Ok";
		SendInfoToServer(info);
		printf("%s",friend_request_name.c_str());
		printf("已经成为您的好友！\n");
		myself.friendlist.push_back(friend_request_name);
	}
	else
	{
		string info = "&addfriend_response&"+friend_request_name+"&Fail";
		SendInfoToServer(info);
		printf("您已拒绝");
		printf("%s",friend_request_name.c_str());
		printf("成为您的好友！\n");
	}
}

void WeChatClient::FileTransfer_Confirm(string request_name, string file_name){
	printf("输入1同意接收，输入2拒绝接收\n");
	char ch = getchar();
	if ( ch == '1' )
	{
		string info = "&filetrans_response&"+request_name+"&Ok";
		SendInfoToServer(info);
		printf("正在接收文件...\n");
		FileReceive(file_name);
	}
	else
	{
		string info = "&filetrans_response&"+request_name+"&Fail";
		SendInfoToServer(info);
		printf("您已拒绝");
		printf("%s",request_name.c_str());
		printf("向您传输文件！\n");
	}
}

void WeChatClient::FileTransfer(string friend_name, string file_name){
    char buffer[BufferSize+14];
    FILE * fp = fopen(file_name.c_str(),"rb");
    if(NULL == fp )
    {
        printf("文件未找到！\n");
	return;
    }
    else
    {
	printf("正在传输文件...\n");
        bzero(buffer, BufferSize);
	for ( int i = 1 ; i < 13; i++)
		buffer[i]='f';
	buffer[0]='&';
	buffer[13]='&';
        int file_block_length = 0;
        while((file_block_length = fread(buffer+14,sizeof(char),BufferSize,fp))>0)
        {
            printf("file_block_length = %d\n",file_block_length);
            string buffer_to_send(buffer, sizeof(buffer));
            if(send(sockfd,buffer_to_send.c_str(),file_block_length + 14,0)<0)
            {
                printf("Send File:\t%s Failed\n", file_name.c_str());
                break;
            }
            bzero(buffer, BufferSize);
	    for ( int i = 1 ; i < 13; i++)
		buffer[i]='f';
	    buffer[0]='&';
	    buffer[13]='&';
        }                                                                   
        printf("File:\t%s Transfer Finished\n",file_name.c_str());
	
    	printf("传输成功!\n");
	fclose(fp);
    }
}

void WeChatClient::FileReceive(string file_name){
	char buffer[BufferSize];
	string filename = "/home/star/Desktop/"+file_name;
	FILE * fp = fopen(filename.c_str(),"wb");
	if(NULL == fp )
	    {
		printf("File:\t%s Can Not Open To Write\n", filename.c_str());
		return;
	    }
	while(1){

		FD_ZERO(&readSet);
		FD_SET(0, &readSet);
		FD_SET(sockfd, &readSet);

		int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

		//如果从套接字上接收到数据
		if(FD_ISSET(sockfd, &readSet)){
		    memset(buffer, 0, sizeof(buffer));
		    
		    int length = recv(sockfd, buffer, sizeof(buffer), 0);
		    cout << 4 << 'a' << length << endl;
		    if(length > 0){
			int write_length = fwrite(buffer,sizeof(char),length,fp);
			if (write_length<length)
				{
				    printf("File:\t%s Write Failed\n", filename.c_str());
				    break;
				}
    }
		    //bzero(buffer,BufferSize);  
		}
		else break;
	}
	fclose(fp);
	printf("接收成功！\n");
	return;
}


void WeChatClient::start(int argc, char * argv[]){

    const char *ip = argv[1];
    int port = atoi(argv[2]);

    //创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        cerr << "socket error" << endl;
        return;
    }

    //获得服务器监听的地址
    sockaddr_in serverAddr;
    bzero(&serverAddr, sizeof(serverAddr));

    serverAddr.sin_port = htons(port);
    serverAddr.sin_family = AF_INET;
    inet_pton(AF_INET, ip, &serverAddr.sin_addr);

    //连接到服务器
    int ret = connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if(ret < 0){
        cerr << "connect error" << endl;
        return;
    }
    //server_sockfd = sockfd;

    return;
}

void WeChatClient::SendInfoToServer(string info)
{
	write(sockfd, info.c_str(), info.length());
}

string WeChatClient::AcceptInfoFromConsole()
{
	char buffer[BufferSize];
	while(1){

        FD_ZERO(&readSet);
        FD_SET(0, &readSet);
        FD_SET(sockfd, &readSet);

        int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

        //如果终端上可读
        if(FD_ISSET(0, &readSet)){
            memset(buffer, 0, sizeof(buffer));

            //从终端读取数据
            int num = read(0, buffer, sizeof(buffer));

            if(buffer[num-1] == '\n'){
                buffer[num-1] = '\0';
            }

            // if(strcmp(buffer, "quit") == 0){
            //     break;
            // }
            //将数据写入到套接字
            return buffer;
        }
        
    }
}

string WeChatClient::AcceptInfoFromServer()
{
	char buffer[BufferSize];
	while(1){

        FD_ZERO(&readSet);
        FD_SET(0, &readSet);
        FD_SET(sockfd, &readSet);

        int ret = select(sockfd+1, &readSet, NULL, NULL, 0);

        //如果从套接字上接收到数据
        if(FD_ISSET(sockfd, &readSet)){
            memset(buffer, 0, sizeof(buffer));
            
            int num = recv(sockfd, buffer, sizeof(buffer), 0);
            if(num > 0){
                //write(0, buffer, strlen(buffer));
                //cout << endl;
		//printf("%s\n",buffer);
                return buffer;
            }
        }
        
    }
}

vector<string> WeChatClient::Decode(string s)
{
    vector<string> s_decode;
    for (int i = s.length(); i >= 0; i--)
        if ( s[i]=='&' )
        {
            s_decode.insert(s_decode.begin(),s.substr(i+1,s.length()-i-1));
            s = s.substr(0,i);
        }
    return s_decode;
}

vector<string> WeChatClient::GetFriendFromServer(){
	string info = "&getFriend";
	write(sockfd, info.c_str(), info.length());
	string response = AcceptInfoFromServer();
	vector<string> friendslist = Decode(response);
	friendslist.erase(friendslist.begin());
//printf("%s\n",);
	return friendslist;
}















